package com.hubing.cloud.app.web.es;

import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Searcher {

	private static final Logger logger = LoggerFactory.getLogger(Searcher.class);

	private int ITEMS_PER_SEARCH = 20;

	private QueryBuilder queryBuilder = null;
	private Long         numSearched  = -1L;
	private int          currentPage  = 0;
	private Long         rows         = 0L;
	private String       sortedField  = null;

	public Searcher(Client elasticSearchCilent, QueryBuilder queryBuilder, String indexName, String typeName) {
		this.queryBuilder = queryBuilder;
		this.rows = getCount(elasticSearchCilent, indexName, typeName);
	}

	public Searcher(Client elasticSearchCilent, QueryBuilder queryBuilder, String sortedField, String indexName,
			String typeName) {
		this.queryBuilder = queryBuilder;
		this.sortedField = sortedField;
		this.rows = getCount(elasticSearchCilent, indexName, typeName);
	}

	//	public Searcher(Client elasticSearchCilent, QueryBuilder queryBuilder, String sortedField, int itemsPerPage,
	//			String indexName, String typeName) {
	//		this.queryBuilder = queryBuilder;
	//		this.sortedField = sortedField;
	//		if (itemsPerPage > 0 && itemsPerPage <= 10000) {
	//			this.ITEMS_PER_SEARCH = itemsPerPage;
	//		}
	//		this.rows = getCount(elasticSearchCilent, indexName, typeName);
	//		this.rows = this.rows > Integer.MAX_VALUE ? Integer.MAX_VALUE : this.rows;
	//	}

	public Searcher(Client elasticSearchCilent, QueryBuilder queryBuilder, String sortedField, int itemsPerPage,
			int currentPage, String indexName, String typeName) {
		this.queryBuilder = queryBuilder;
		this.sortedField = sortedField;
		if (itemsPerPage > 0 && itemsPerPage <= 10000) {
			this.ITEMS_PER_SEARCH = itemsPerPage;
		}
		this.currentPage = currentPage;
		this.rows = getCount(elasticSearchCilent, indexName, typeName);
		Long maxValue = Integer.valueOf(Integer.MAX_VALUE).longValue();
		this.rows = this.rows > maxValue ? maxValue : this.rows;
	}

	public Searcher(QueryBuilder queryBuilder, Long rows, String sortedField) {
		this.queryBuilder = queryBuilder;
		this.rows = rows;
		this.sortedField = sortedField;
	}

	/**
	 * 默认的计数操作，针对配置文件中默认的typename进行
	 *
	 * @return
	 */

	public Long getCount(Client elasticSearchCilent, String indexName, String typeName) {
		if (numSearched == -1) {
			SearchResponse searchResponse = elasticSearchCilent.prepareSearch(indexName)
															   .setTypes(typeName)
															   .setQuery(queryBuilder)
															   .setFrom(0)
															   .execute()
															   .actionGet();
			numSearched = searchResponse.getHits().getTotalHits().value;
		}
		return numSearched;
	}

	public boolean hasNext(Client elasticSearchCilent, String indexName, String typeName) {
		return ((this.currentPage * ITEMS_PER_SEARCH) < getCount(elasticSearchCilent, typeName, indexName) &&
				(this.currentPage * ITEMS_PER_SEARCH) < this.rows);
	}

	public List<Map<String, Object>> next(Client elasticSearchCilent, String indexName, String typeName) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		if (this.rows == 0) {
			return result;
		}

		SearchRequestBuilder searchRequestBuilder = elasticSearchCilent.prepareSearch(indexName)
																	   .setTypes(typeName)
																	   .setQuery(queryBuilder)
																	   .setFrom(currentPage * ITEMS_PER_SEARCH)
																	   .setSize(ITEMS_PER_SEARCH);
		if (sortedField != null) {
			searchRequestBuilder.addSort(sortedField, SortOrder.DESC);
		}
		SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
		SearchHits hits = searchResponse.getHits();
		if (hits != null) {
			SearchHit[] searchHits = hits.getHits();
			for (SearchHit searchHit : searchHits) {
				result.add(formatMap(searchHit.getSourceAsMap()));
			}
			this.currentPage++;
		}
		return result;
	}

	private Map<String, Object> formatMap(Map<String, Object> elasticSearchMap) {
		elasticSearchMap.remove("_version_");
		return elasticSearchMap;
	}

	public Long getRows() {
		return rows;
	}

	public Map<String, Object> findById(Client elasticSearchCilent, String indexName, String typeName, String id) {
		GetResponse response =
				elasticSearchCilent.prepareGet(indexName, typeName, id).get();
		if (response.isExists()) {
			return response.getSourceAsMap();
		} else {
			return null;
		}
	}
}
